#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <atomic>
#include <boost/algorithm/string.hpp>

// 工具，提供各种工具方法
namespace Tool_NS
{
    // 默认临时文件的路径
    const static std::string path = "./temp/";

    // 路径工具，提供拼接路径和后缀的功能
    class PathTool
    {
    public:
        // 添加路径和后缀
        static std::string Add_Path_And_Suffix(const std::string &file_name, const std::string &suffix)
        {
            std::string path_name = path;
            path_name += file_name;
            path_name += suffix;

            return path_name;
        }

        // 编译时所需要的临时文件
        // 构建源文件的完整路径+后缀名
        static std::string Src(const std::string &file_name)
        {
            return Add_Path_And_Suffix(file_name, ".cpp");
        }
        // 构建可执行程序的完整路径+后缀名
        static std::string Exe(const std::string &file_name)
        {
            return Add_Path_And_Suffix(file_name, ".exe");
        }
        // 构建编译错误的文件+完整路径+后缀名
        static std::string CompileError(const std::string &file_name)
        {
            return Add_Path_And_Suffix(file_name, ".compile_error");
        }

        // 运行时所需要的临时文件
        // 构建标准输入的完整路径+后缀名
        static std::string Stdin(const std::string &file_name)
        {
            return Add_Path_And_Suffix(file_name, ".stdin");
        }
        // 构建标准输出的完整路径+后缀名
        static std::string Stdout(const std::string &file_name)
        {
            return Add_Path_And_Suffix(file_name, ".stdout");
        }
        // 构建标准错误的完整路径+后缀名
        static std::string Stderr(const std::string &file_name)
        {
            return Add_Path_And_Suffix(file_name, ".stderr");
        }
    };

    class TimeTool
    {
    public:
        // 获取秒级别的时间戳
        static std::string GetTimeStamp()
        {
            // 获取时间戳和时区，但我们并需要时区的信息
            struct timeval time;
            gettimeofday(&time, nullptr);

            return std::to_string(time.tv_sec);
        }

        // 获取毫秒级别的时间戳
        static std::string GetTimeMS()
        {
            struct timeval time;
            gettimeofday(&time, nullptr);

            // 秒级别的时间戳*1000 + 微秒级别的时间戳/1000 = 毫秒级别的时间戳
            return std::to_string(time.tv_sec * 1000 + time.tv_usec / 1000);
        }
    };

    class FileTool
    {
    public:
        static bool IsFileExists(const std::string &path_name)
        {
            // 文件属性信息，但是这里我们不需要
            struct stat st;

            // stat函数用于获取文件属性信息，若获取失败则说明文件不存在
            if (stat(path_name.c_str(), &st) == 0)
            {
                return true;
            }

            return false;
        }

        // 由于可能会有多个客户同时提交代码，所以这里需要唯一的文件名，形成唯一的一些临时文件
        static std::string UniqFileName()
        {
            static std::atomic_int id(0);

            std::string UniqTime = TimeTool::GetTimeMS();
            std::string UniqId = std::to_string(id);

            return UniqTime + "_" + UniqId;
        }

        // 将代码写入到指定的文件中
        static bool WriteFile(const std::string &path_name, const std::string &code)
        {
            std::ofstream out(path_name);

            if (!out.is_open())
            {
                return false;
            }

            out.write(code.c_str(), code.size());

            return true;
        }

        // 从指定的文件中读取代码
        // 程序中可能会有保存行分隔符的需要，这里参数keep就是默认不保存行分隔符
        // 若是需要，将keep设置为true，下面的代码会默认在每一行最后再将行分隔符添加回来
        static bool ReadFile(const std::string &path_name, std::string *code, bool keep = false)
        {
            *code = "";

            std::ifstream in(path_name);

            if (!in.is_open())
            {
                return false;
            }

            // getline不会保存行分隔符，getline内部重载了强制类型转换，所以可以直接作为while/if的条件
            std::string line;
            while (std::getline(in, line))
            {
                *code += line;
                *code += (keep ? "\n" : "");
            }

            return true;
        }
    };

    class StringTool
    {
    public:
        /**********************************************
         * str : 需要分割的字符串
         * v_sub : 将分割后字符串放到vector中
         * sep : 特定的分隔符
         */
        static void SplitString(const std::string &str, std::vector<std::string> *v_sub, const std::string &sep)
        {
            // v_str : 将分割后的子串放到v_str中
            // str : 需要分割的字符串
            // boost::is_any_of(":") : 是boost::is_any_of参数字符串中的其中一个字符就是分隔符
            // boost::token_compress_on : 压缩，以上面为例，多个分隔符连在一起时，并且它们之间没有其他内容，就不保留
            boost::split((*v_sub), str, boost::is_any_of(sep), boost::token_compress_on);
        }
    };
}